package ltd.hyangyang.rpc.core.proxy;

import ltd.hyangyang.rpc.common.bean.Request;
import ltd.hyangyang.rpc.common.bean.Response;
import ltd.hyangyang.rpc.common.bean.RpcConfig;
import ltd.hyangyang.rpc.common.bean.RpcProperties;
import ltd.hyangyang.rpc.common.exception.RpcException;
import ltd.hyangyang.rpc.common.extension.ExtensionLoader;
import ltd.hyangyang.rpc.common.utils.SingletonFactory;
import ltd.hyangyang.rpc.core.anno.ServiceReference;
import ltd.hyangyang.rpc.core.registory.DiscoverService;
import ltd.hyangyang.rpc.core.remoting.client.RpcClientNetty;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

public class ClientInvocationHandler implements InvocationHandler {

    RpcConfig rpcConfig ;

    RpcProperties rpcProperties;

    ServiceReference serviceDiscover;

    ConcurrentHashMap<String,Integer> preNode = new ConcurrentHashMap<>();

    ClientInvocationHandler(RpcConfig rpcConfig){
        //实例化RpcConfig
        this.rpcConfig = rpcConfig;

        //获取单例对象
        rpcProperties = SingletonFactory.getSingletonBean(RpcProperties.class);
    }

    /**
     *
     * @param proxy：动态代理生成的代理类
     * @param method：与代理类对象调用的方法相对应
     * @param args ： 当前method的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在这里实现具体的业务逻辑
        //1.服务发现
        List<String> nodes = toServiceDiscover();

        //使用netty向服务端发送消息
        String node = loadBalance(nodes);
//        RpcClientNetty clientNetty = new RpcClientNetty(node);

        RpcClientNetty rpcClientNetty = SingletonFactory.checkSingletonExist(RpcClientNetty.class);
        if (rpcClientNetty==null){
            rpcClientNetty = SingletonFactory.getConstructor(RpcClientNetty.class,String.class).newInstance(node);
        }
        Request request = buildRequest(method,node,args);
        CompletableFuture<Response> completableFuture = rpcClientNetty.sendMessage(request);
        Response response = completableFuture.get();
        //返回ServerMessage
        Object data = response.getData();
        return data;
    }

    private String loadBalance(List<String> nodes) {
        if (nodes.size()==0){
            throw new RpcException("没有找到可用的实现");
        }
        //负载均衡实现
        //TODO 当前对于node节点使用第一个节点，后续实现负载均衡的功能
        String serviceName = rpcConfig.getServiceName();
        Integer preNodeIndex = preNode.get(serviceName);
        Integer nodeIndex=0;
        if (preNodeIndex!=null&&preNodeIndex<nodes.size()-1){
            nodeIndex = preNodeIndex+1;
        }else {
            nodeIndex=0;
        }
        preNode.put(serviceName,nodeIndex);
        return nodes.get(nodeIndex);
    }


    public Request buildRequest(Method method,String node,Object[] args){
        String serviceName = this.rpcConfig.getServiceName();
        return Request.builder()
                .msgType("custom")
                .methodName(method.getName())
                .canonicalName(serviceName)
                .parameters(args)
                .parameterTypes(method.getParameterTypes())
                .serverAddr(node)
                .returnType(method.getReturnType())
                .requestId(UUID.randomUUID().toString())
                .build();

    }

    public List<String> toServiceDiscover() throws Exception {
        ExtensionLoader<DiscoverService> extensionLoader = ExtensionLoader.getExtensionLoader(DiscoverService.class);
        DiscoverService discoverService = extensionLoader.getExtension();
//        while (iterator.hasNext()){
//            DiscoverService discoverService = iterator.next();
//            String canonicalName = discoverService.getClass().getCanonicalName();
//            if (rpcProperties.getRegistryType().equals(RegistryEnum.ZOOKEEPER.registryType)&&ZookDisCoverService.class.getCanonicalName().equals(canonicalName)){
//                //为zookeeper,
//                discoverService.discover(rpcConfig);
//            }else if (rpcProperties.getRegistryType().equals(RegistryEnum.NACOS.registryType)) {
//
//            }
//        }

        List<String> nodes = discoverService.discover(rpcConfig);
        return nodes;
    }
}
